package com.qijian.tool.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONField;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qijian.common.exception.ServiceException;
import com.qijian.common.utils.uuid.IdUtils;
import com.qijian.tool.domain.Msg;
import com.qijian.tool.domain.query.MsgQuery;
import com.qijian.tool.domain.vo.DictData;
import com.qijian.tool.domain.vo.MsgVo;
import com.qijian.tool.mapper.MsgMapper;
import com.qijian.tool.service.IMsgService;
import com.qijian.tool.utils.QyWxUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 我的信息Service业务层处理
 *
 * @author qijian
 * @date 2022-06-06
 */
@Service
@RequiredArgsConstructor
public class MsgServiceImpl extends ServiceImpl<MsgMapper, Msg> implements IMsgService {

    private static final Logger log = LoggerFactory.getLogger(MsgServiceImpl.class);


    private final MsgMapper msgMapper;


    @Value("${wechat.msg-white-list-source-type}")
    private String wxMsgSourceType;

    @Value("${wechat.flow-white-list-source-type}")
    private String wxFlowSourceType;

    @Autowired
    private QyWxUtil qyWxUtil;

    @Value("${uniPush.url}")
    private String url;


    public boolean save(Msg entity, boolean sendToWx, boolean isFlow) {
        int enable = msgMapper.enableWxFlag();
        if (enable == 1) {
            sendToWx(entity, sendToWx, isFlow);
        }
        return this.save(entity);
    }

    public boolean saveBatch(Collection<Msg> entityList, boolean sendToWx, boolean isFlow) {
        int enable = msgMapper.enableWxFlag();
        if (enable == 1) {
            sendToWxBatch(entityList, sendToWx, isFlow);
        }
        if (CollectionUtils.isNotEmpty(entityList)) {
            entityList.stream().forEach(e -> {
                if ("otd_record_ncr".equals(e.getSourceType())) {
                    e.setUrl(e.getUrl() + "&flowType=deliveryNcr");
                }
            });
        }
        return this.saveBatch(entityList);
    }

    @Override
    public String rePush2APP(Long id) {

        Msg msg = this.msgMapper.getFullById(id);

        DictData conf = getPushConfig(msg.getSourceType());
        if (null == conf) {
            throw new ServiceException("当前类型[" + msg.getSourceType() + "] 没有配置APP推送");
        }

        String errMsg = this.push2APP(msg, null, false, conf);

        if (null == errMsg) {
            return "推送成功";
        }
        return "推送失败";
    }

    private String callWxService(Msg entity, boolean isFlow) {
        if (isFlow) {
            return qyWxUtil.sendFlowMsg2Wx(entity);
        } else {
            return qyWxUtil.sendMsg2Wx(entity);
        }
    }

    private String callWxServiceBatch(Collection<Msg> entityList, boolean isFlow) {
        if (isFlow) {
            return qyWxUtil.sendFlowMsg2WxBatch(new ArrayList<Msg>(entityList));
        } else {
            return qyWxUtil.sendMsg2WxBatch(new ArrayList<Msg>(entityList));
        }

    }

    private void sendToWx(Msg entity, boolean sendToWx, boolean isFlow) {
        if (sendToWx) {
            // sourceType 进行白名单过滤
            if (validSourceType(isFlow, Collections.singletonList(entity))) {
                List<Msg> wxInfos = msgMapper.queryUserWxUserIds(Collections.singleton(entity.getUserId()));
                entity.setReceiverName(wxInfos.get(0).getReceiverName());
                entity.setWxUserId(wxInfos.get(0).getWxUserId());
                String res = callWxService(entity, isFlow);
                entity.setRemark(res);
            } else {
                entity.setRemark("未开启 sourceType的白名单或者消息为空");
            }
        }
    }


    /**
     * 发送给消息给微信 (白名单过滤->获取微信用户id ->发送给微信)
     *
     * @param entityList
     * @param sendToWx
     * @param isFlow
     */
    private void sendToWxBatch(Collection<Msg> entityList, boolean sendToWx, boolean isFlow) {
        // 需要发送给微信
        if (sendToWx) {
            // sourceType 进行白名单过滤
            if (validSourceType(isFlow, entityList)) {
                Set<Long> userIds = entityList.stream().map(Msg::getUserId).collect(Collectors.toSet());
                List<Msg> wxInfos = msgMapper.queryUserWxUserIds(userIds);

                if (wxInfos.stream().filter(e -> StringUtils.isEmpty(e.getWxUserId())).collect(Collectors.toList()).size() > 0) {
                    throw new ServiceException(wxInfos.stream().filter(e -> StringUtils.isEmpty(e.getWxUserId())).map(n -> n.getReceiverName()).collect(Collectors.joining()) + "未配置企业微信用户id");
                }
                Map<Long, List<Msg>> userInfoMap = wxInfos.stream().collect(Collectors.groupingBy(Msg::getUserId));
                Iterator<Msg> iterator = entityList.iterator();
                while (iterator.hasNext()) {
                    Msg msg = iterator.next();
                    if (msg == null || CollectionUtils.isEmpty(userInfoMap.get(msg.getUserId()))) {
                        iterator.remove();
                        continue;
                    }
                    msg.setReceiverName(userInfoMap.get(msg.getUserId()).get(0).getReceiverName());
                    msg.setWxUserId(userInfoMap.get(msg.getUserId()).get(0).getWxUserId());
                }
                String res = callWxServiceBatch(entityList, isFlow);
                entityList.stream().forEach(e -> {
                    e.setRemark(res);
                });
            } else {
                entityList.stream().forEach(e -> {
                    e.setRemark("未开启 sourceType的白名单");
                });
            }
        }
    }

    private boolean validSourceType(boolean isFlow, Collection<Msg> entityList) {
        if (CollectionUtils.isEmpty(entityList)) {
            return false;
        }
        String whiteListStr = isFlow ? wxFlowSourceType : wxMsgSourceType;

        if (StringUtils.isEmpty(whiteListStr)) {
            log.info("################################ 未开启 微信消息/流程 sourceType 白名单 ################################");
            return false;
        }

        // 获取白名单
        List<String> whiteList = Arrays.asList(whiteListStr.split(","));

        String sourceType = entityList.stream().findFirst().get().getSourceType();

        if (whiteList.contains(sourceType)) {
            return true;
        }
        log.info("################################ sourceType:[" + sourceType + "]未开启 微信消息/流程的  白名单 ################################");
        return false;
    }


    public Page<MsgVo> pageVo(Page<MsgVo> page, QueryWrapper<MsgQuery> queryWrapper) {
        return baseMapper.selectVo(page, queryWrapper);
    }


    @Override
    public boolean save(Msg entity) {
        // 统一设置 version
        String version = LocalDateTime.now().toString();
        entity.setVersion(version);

        Boolean flag = super.save(entity);


        DictData conf = getPushConfig(entity.getSourceType());
        if (null == conf) {
            return flag;
        }

        String errMsg = this.push2APP(entity, version, false, conf);

        if (null == errMsg) {
            return flag;
        }
        return true;
    }

    @Override
    public boolean saveBatch(Collection<Msg> entityList) {
        // 版本号
        if (CollectionUtils.isEmpty(entityList)) {
            return true;
        }
        String version = LocalDateTime.now().toString();
        entityList.forEach(entity -> entity.setVersion(version));
        Boolean flag = super.saveBatch(entityList);


        Msg entity = entityList.stream().findFirst().orElseGet(null);

        if (null == entity) {
            throw new ServiceException("EX01:数据获取失败");
        }

        DictData conf = getPushConfig(entity.getSourceType());
        if (null == conf) {
            return flag;
        }
        String errMsg = this.push2APP(entity, version, true, conf);

        if (null == errMsg) {
            return flag;
        }
        return true;
    }

    public DictData getPushConfig(String sourceType) {
        return msgMapper.getDataByDictKey(sourceType);
    }


    /**
     * unionApp 消息推送
     *
     * @param msg     消息
     * @param version 版本号
     * @param isBatch 是否批量推送
     */
    public String push2APP(Msg msg, String version, boolean isBatch, DictData config) {

        String[] clientIdArr;
        String errMsg = null;
        if (!isBatch) {
            String clientId = msgMapper.getClientIdByUserId(msg.getUserId());

            if (StringUtils.isBlank(clientId)) {
                errMsg = "Warning: app 推送消息失败[" + msg.getReceiverName() + "]没有找到登录的手机ClientId";
                log.warn(errMsg);
                return errMsg;
            }

            clientIdArr = new String[]{clientId};
        } else {
            List<String> clientIds = msgMapper.getClientIdsByVersion(version);
            if (CollectionUtils.isEmpty(clientIds)) {
                errMsg = "Warning:app 推送消息失败没有找到登录的手机ClientId";
                log.warn(errMsg);
                return errMsg;
            }
            clientIdArr = clientIds.toArray(new String[clientIds.size()]);
        }


        //获取clientId
        UnionPushMsg unionPushMsg = new UnionPushMsg();
        unionPushMsg.setRequestId(String.valueOf(IdUtils.singletonSnowId()));
        unionPushMsg.setClientIds(clientIdArr);
        if(StrUtil.length(msg.getTitle())>=50){
            unionPushMsg.setTitle(StrUtil.sub(msg.getTitle(),0,40)+"...");
        }else{
            unionPushMsg.setTitle(msg.getTitle());
        }
        if(StrUtil.length(msg.getContent())>256){
            unionPushMsg.setContent(StrUtil.sub(msg.getContent(),0,200)+"...");
        }else{
            unionPushMsg.setContent(msg.getContent());
        }

        unionPushMsg.setPayload(getUnionPayload(msg, config.getRemark()));
        //创建POST请求
        HttpRequest request = HttpRequest.post(url + "/pushMessage");
        request.header("Content-Type", "application/json");

        request.body(JSONObject.toJSONString(unionPushMsg));

        log.info("##########==>> UinPush消息" + JSONObject.toJSONString(unionPushMsg));

        // 发送请求并获取响应
        HttpResponse response = request.execute();
        String resp = response.body();
        JSONObject respJson = JSONObject.parseObject(resp);

        String errCode = respJson.getString("errCode");
        if ("0".equals(errCode)) {
            log.info("##########==>> UinPush消息RESP" + respJson.toString());
        } else {
            errMsg = " Ex01:app 推送消息失败" + respJson.toString();
            log.error("##########==>> Ex01:app 推送消息失败" + respJson.toString());
        }
        return errMsg;
    }

    public UnionPayLoad getUnionPayload(Msg msg, String remark) {
        UnionPayLoad unionPayLoad = new UnionPayLoad();
        unionPayLoad.setId(msg.getSourceId());
        unionPayLoad.setProcessInstanceId(msg.getProcessInstanceId());
        unionPayLoad.setSourceType(msg.getSourceType());
        unionPayLoad.setTaskId(msg.getTaskId());
        unionPayLoad.setProcessInstanceName(msg.getTitle());
        unionPayLoad.setCreateBy(msg.getCreateByName());
        unionPayLoad.setCreateTime(msg.getCreateTime());
        unionPayLoad.setConfigRemark(replaceUrl(remark, msg));
        return unionPayLoad;
    }

    public String replaceUrl(String remark, Msg msg) {
        if (null == remark) return null;

        JSONObject json = JSONObject.parseObject(remark);

        if (null == json) return null;

        String path = json.getString("path");

        if (null == path) return remark;


        // 正则表达式匹配花括号内的参数名
        Pattern pattern = Pattern.compile("\\{(\\w+)\\}");
        Matcher matcher = pattern.matcher(path);

        // 遍历所有匹配的参数名
        while (matcher.find()) {
            try {
                // 获取参数名对应的字段
                Field field = msg.getClass().getDeclaredField(matcher.group(1));
                // 确保可以访问私有字段
                field.setAccessible(true);
                // 获取字段的值
                Object value = field.get(msg);
                // 替换URL中的占位符
                path = path.replace("{" + matcher.group(1) + "}", String.valueOf(value));
            } catch (NoSuchFieldException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        json.put("path", path);
        return json.toJSONString();
    }
}

@Data
class UnionPushMsg {

    @JSONField(name = "request_id")
    public String requestId;

    @JSONField(name = "push_clientid")
    public String[] clientIds;

    @JSONField(name = "force_notification")
    public boolean forceNotification;

    @JSONField(name = "title")
    public String title;

    @JSONField(name = "content")
    public String content;

    @JSONField(name = "payload")
    public UnionPayLoad payload;
}

@AllArgsConstructor
@Data
@NoArgsConstructor
class UnionPayLoad {
    @JSONField(name = "id")
    public String id;

    @JSONField(name = "processInstanceId")
    public String processInstanceId;

    @JSONField(name = "processInstanceName")
    public String processInstanceName;

    @JSONField(name = "sourceType")
    public String sourceType;

    @JSONField(name = "taskId")
    public String taskId;

    @JSONField(name = "createBy")
    public String createBy;

    @JSONField(name = "createTime")
    public Date createTime;


    @JSONField(name = "configRemark")
    public String configRemark;

}
